package com.wex.app;

import com.wex.app.tools.*;
import com.wex.jni.JniFFmpeg;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.media.codec.*;

import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class CodecTestAbility extends Ability {

    private TextField textField;
    private Checkbox cbSwitch;
    private Button btnEncAAC;
    private Button btnDecAAC;
    private Button btnEncH264;
    private Button btnDecH264;
    private Button btnEncH264_480p;
    private Button btnDecH264_480p;
    private Button btnExit;

    private LogUtils log;

    private CodecButtonWorker cbw1, cbw2, cbw3, cbw4;

    private MediaCodec.Context mediaCodec = null;
    private int mediaCodecState = 0;
    private boolean isEncode = false;
    private RandomAccessFile readFile = null;
    private JniFFmpeg.H26xFiFo h26xFiFo = null;

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            MediaCodec.Packet outPkg = new MediaCodec.Packet();
            MediaCodec.Packet inPkg = new MediaCodec.Packet();
            inPkg.dwSize = mediaCodec.dwWidth * mediaCodec.dwHeight * 3 / 2;
            inPkg.lpData = new byte[inPkg.dwSize];

            //统计
            int resultPushBytes = 0;
            int resultGainBytes = 0;
            int resultFrame = 0;
            long resultMsBegin = System.currentTimeMillis();
            long resultMsEnd = 0;
            byte[] resultReadSample = new byte[10];
            int resultFrameMax = 100; //达到该输出帧数时自动停止

            while (mediaCodecState == 1) {

                //帧抠取
                if (readFile != null && h26xFiFo != null) {
                    if (h26xFiFo.Gaps() > 0) {
                        byte[] readDat = new byte[h26xFiFo.Gaps()];
                        int ret = FileTools.read(readFile, readDat);
                        h26xFiFo.Set(readDat, ret);
                    }
                    inPkg.lpData = h26xFiFo.Get();
                    inPkg.dwSize = inPkg.lpData.length;
                }

                int ret = isEncode ? MediaCodec.Encode(mediaCodec, inPkg, outPkg) : MediaCodec.Decode(mediaCodec, inPkg, outPkg);
                if (ret == 0) {
                    if (outPkg.dwSize > 9)
                        log.append("mediaCodec process: " + outPkg.dwSize + " - "
                                + Integer.toHexString(outPkg.lpData[0] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[1] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[2] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[3] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[4] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[5] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[6] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[7] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[8] & 0xFF) + " "
                                + Integer.toHexString(outPkg.lpData[9] & 0xFF) + " "
                                + "\n");
                    else
                        log.append("mediaCodec process: " + outPkg.dwSize + "\n");

                    //统计
                    resultPushBytes += inPkg.dwSize;
                    resultGainBytes += outPkg.dwSize;
                    if (outPkg.dwSize > 0) {
                        resultFrame += 1;
                        //采样
                        for (int i = 0; i < resultReadSample.length && i < outPkg.dwSize; i++)
                            resultReadSample[i] = outPkg.lpData[i];
                    }
                    //结束
                    if (resultFrame >= resultFrameMax)
                        break;
                }
                else
                    log.append("mediaCodec process: failed " + ret + "\n");
            }

            if (isEncode)
                MediaCodec.CloseEncoder(mediaCodec);
            else
                MediaCodec.CloseDecoder(mediaCodec);
            mediaCodec = null;

            //统计
            resultMsEnd = System.currentTimeMillis();
            String info = "\n" + (isEncode ? "encode" : "decode") + " - result: ****************************************\n"
                    + "  frame: " + resultFrame + "  pushBytes: " + resultPushBytes + "  gainBytes: " + resultGainBytes + "\n"
                    + "  readSample(hex): "
                    + Integer.toHexString(resultReadSample[0] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[1] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[2] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[3] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[4] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[5] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[6] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[7] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[8] & 0xFF) + " "
                    + Integer.toHexString(resultReadSample[9] & 0xFF) + "\n"
                    + "  timeBegin: " + resultMsBegin + "ms  timeEnd: " + resultMsEnd + "ms \n"
                    + "  timeInterval: " + (resultMsEnd - resultMsBegin) + "ms \n"
                    + "************************************************ \n\n";
            log.appendAll(info);

            FileTools.close(readFile);
            readFile = null;
            h26xFiFo = null;

            mediaCodecState = 0;
        }
    };

    private void btnEncAACCallback(Component component)
    {
        if (mediaCodecState == 0) {
            mediaCodec = new MediaCodec.Context();
            mediaCodec.dwWidth = 1920;
            mediaCodec.dwHeight = 1080;
            mediaCodec.dwFrameRate = 30;
            mediaCodec.dwBitrate = 10000000;
            int ret = MediaCodec.InitEncoder(mediaCodec);
            if (ret != 0) {
                mediaCodec = null;
                log.appendAll("mediaEncode start failed " + ret + "\n");
            }
            else {
                log.appendAll("mediaEncode start success \n");

                isEncode = true;
                mediaCodecState = 1;
                Thread t = new Thread(runnable);
                t.start();
            }
        }
        else if (mediaCodecState == 1)
            mediaCodecState = 2;
        else
            log.appendAll("mediaEncode busy ... \n");
    }
    private void btnDecAACCallback(Component component)
    {
        if (mediaCodecState == 0) {
            readFile = FileTools.openReader(getContext(), "1080P.h264");
            if (readFile == null) {
                log.appendAll("未找到文件: /Android/media/com.wex.app/1080P.h264 \n");
                return;
            }
            mediaCodec = new MediaCodec.Context();
            mediaCodec.dwWidth = 1920;
            mediaCodec.dwHeight = 1080;
            mediaCodec.dwFrameRate = 30;
            mediaCodec.dwBitrate = 10000000;
            int ret = MediaCodec.InitDecoder(mediaCodec);
            if (ret != 0) {
                mediaCodec = null;
                FileTools.close(readFile);
                readFile = null;
                log.appendAll("mediaDecode start failed " + ret + "\n");
            }
            else {
                log.appendAll("mediaDecode start success \n");

                h26xFiFo = new JniFFmpeg.H26xFiFo(1024 * 500, 3);

                isEncode = false;
                mediaCodecState = 1;
                Thread t = new Thread(runnable);
                t.start();
            }
        }
        else if (mediaCodecState == 1)
            mediaCodecState = 2;
        else
            log.appendAll("mediaDecode busy ... \n");
    }

    //获取ui控件控制符
    private void initComponents() {
        textField = (TextField) findComponentById(ResourceTable.Id_codec_test_text);
        cbSwitch = (Checkbox) findComponentById(ResourceTable.Id_codec_cb_switch);
        btnEncAAC = (Button) findComponentById(ResourceTable.Id_codec_test_btn_enc_aac);
        btnDecAAC = (Button) findComponentById(ResourceTable.Id_codec_test_btn_dec_aac);
        btnEncH264 = (Button) findComponentById(ResourceTable.Id_codec_test_btn_enc_h264);
        btnDecH264 = (Button) findComponentById(ResourceTable.Id_codec_test_btn_dec_h264);
        btnEncH264_480p = (Button) findComponentById(ResourceTable.Id_codec_test_btn_enc_h264_480p);
        btnDecH264_480p = (Button) findComponentById(ResourceTable.Id_codec_test_btn_dec_h264_480p);
        btnExit = (Button) findComponentById(ResourceTable.Id_codec_test_btn_exit);

//        btnEncAAC.setEnabled(false);
//        btnEncAAC.setAlpha(0.5f);
//        btnDecAAC.setEnabled(false);
//        btnDecAAC.setAlpha(0.5f);

        btnEncAAC.setText("encode h264(MediaCodec封装)");
        btnDecAAC.setText("decode h264(MediaCodec封装)");
        btnEncAAC.setClickedListener(this::btnEncAACCallback);
        btnDecAAC.setClickedListener(this::btnDecAACCallback);

        log = new LogUtils(getContext());
        Date d = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        log.setLogFile("codec-" + sdf.format(d) + ".txt");
        log.setTextField(textField);

        cbw1 = new CodecButtonWorker(btnEncH264, log, null, null, "btnEncH264");
        cbw1.setCodec(CodecUtils.Type.ENCODE, CodecUtils.Format.H264, 1920, 1080, 30);
        cbw2 = new CodecButtonWorker(btnDecH264, log, "1080P.h264", null, "btnDecH264");
        cbw2.setCodec(CodecUtils.Type.DECODE, CodecUtils.Format.H264, 1920, 1080, 30);
        cbw3 = new CodecButtonWorker(btnEncH264_480p, log, null, null, "btnEncH264_480p");
        cbw3.setCodec(CodecUtils.Type.ENCODE, CodecUtils.Format.H264, 720, 480, 30);
        cbw4 = new CodecButtonWorker(btnDecH264_480p, log, "480P.h264", null, "btnDecH264_480p");
        cbw4.setCodec(CodecUtils.Type.DECODE, CodecUtils.Format.H264, 720, 480, 30);

        btnExit.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                log.appendAll("btnExitCallback\n");
                terminateAbility();
            }
        });
        cbSwitch.setCheckedStateChangedListener(new AbsButton.CheckedStateChangedListener() {
            @Override
            public void onCheckedChanged(AbsButton absButton, boolean b) {
                log.setTextFiledAlwaysOn(b);
            }
        });
    }

    //打印codec能力
    private void showCodecAbility() {
        log.appendAll("\n----->> show support mimes begin <<-----\n");
        List<String> supportedMimes = CodecDescriptionList.getSupportedMimes();
        for (String supportedMime : supportedMimes) {
            log.appendAll(" --> " +
                supportedMime +
                " --> isDec:" +
                CodecDescriptionList.isDecodeSupportedByMime(supportedMime) +
                " --> isEnc:" +
                CodecDescriptionList.isEncodeSupportedByMime(supportedMime) +
                "\n");
        }
        log.appendAll("----->> show support mimes end <<-----\n\n");
    }

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_codec_test_ability);
        //获取ui控件控制符
        initComponents();
        //打印codec能力
        showCodecAbility();
    }

    @Override
    public void onStop() {
        super.onStop();
    }
}
